package edu.caltech.android;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.text.format.Time;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;

/**
 * This class extends the EarthquakeOverlay from PA2AD to be an ItemizedOverlay,
 * with informative displays when an overlay item is tapped.
 * 
 * Portions of this code from Professional Android 2 Application Development,
 * ISBN: 978-0-470-56552-0, copyright Wiley Publishing Inc: 2010, by Reto Meier,
 * published under the Wrox imprint are used by permission of Wiley Publishing,
 * Inc. All rights reserved. This book and the Wrox code are available for
 * purchase or download at www.wrox.com
 * 
 * @author mfaulk
 * 
 */
public class EarthquakeItemizedOverlay extends ItemizedOverlay<QuakeOverlayItem> {
	private static final String TAG = "EarthquakeItemizedOverlay";
	private Cursor quakeCursor;
	private List<QuakeOverlayItem> myOverlays;

	private final Context context;
	private int lastTapped;
	private long animTime;
	Drawable marker;

	/*
	 * Class constructor.
	 */
	public EarthquakeItemizedOverlay(Drawable defaultMarker,
			Cursor quakeCursor, Context context) {
		super(boundCenterBottom(defaultMarker));
		this.context = context;
		myOverlays = new ArrayList<QuakeOverlayItem>();
		populate();
		marker = defaultMarker;
		animTime = -1;
		
		this.quakeCursor = quakeCursor;
		this.quakeCursor.registerDataSetObserver(new DataSetObserver() {
			@Override
			public void onChanged() {
				refreshQuakeLocations();
			}
		});

		refreshQuakeLocations();
	}

	@Override
	protected QuakeOverlayItem createItem(int i) {
		return myOverlays.get(i);
	}

	@Override
	public int size() {
		return myOverlays.size();
	}
	
	/*
	*	Method refreshes earthquake locations, fetching data from the
	*	EarthquakeProvider.
	*/
	private void refreshQuakeLocations() {
		//Log.v(TAG, "refreshQuakeLocations");
		myOverlays.clear();
		if (quakeCursor.moveToFirst()) {
			do {
				// convert to microdegrees for use with GeoPoint
				Double latE6 = quakeCursor
						.getFloat(EarthquakeProvider.LATITUDE_COLUMN) * 1E6;
				Double lngE6 = quakeCursor
						.getFloat(EarthquakeProvider.LONGITUDE_COLUMN) * 1E6;
				float mag = quakeCursor
						.getFloat(EarthquakeProvider.MAGNITUDE_COLUMN);

				long timeMillis = quakeCursor
						.getLong(EarthquakeProvider.DATE_COLUMN);

				GeoPoint g = new GeoPoint(latE6.intValue(), lngE6.intValue());

				// create an OverlayItem and add it to our list
				QuakeOverlayItem quakeOverlay = new QuakeOverlayItem(g,
						"title", "snippet", mag, timeMillis);
				
				myOverlays.add(quakeOverlay);

			} while (quakeCursor.moveToNext());
		}
		populate();
	}
	

	// Handle tap events on overlay icons
	@Override
	protected boolean onTap(int i) {
		QuakeOverlayItem q = myOverlays.get(i);
		DecimalFormat decimalFormat = new DecimalFormat("##.000");

		DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss");

		String toast = "M" + Float.toString(q.getMagnitude()) + "\n" + "("
				+ decimalFormat.format(q.getPoint().getLatitudeE6() / 1E6)
				+ " , "
				+ decimalFormat.format(q.getPoint().getLongitudeE6() / 1E6)
				+ ")" + "\n" + dateFormat.format(new Date(q.getTimeMillis())
				);
		Toast.makeText(context, toast, Toast.LENGTH_LONG).show();
		q.setTapTime((long) q.getAge());
		animTime = -1;
		lastTapped = i;
		return (true);
	}
	
	
	// Overridden method that draw on all markers
	
	@Override
	public boolean draw(Canvas canvas, MapView mapView, 
	        boolean shadow, long when) {
        
        // get the last focused item
        QuakeOverlayItem lastFocused = this.getItem(lastTapped);
        
        // if exists, draw
        if (lastFocused != null) {
        	
        	GeoPoint p = lastFocused.getPoint();
        	
	        //---translate the GeoPoint to screen pixels---
	        Point screenPts = new Point();
	        mapView.getProjection().toPixels(p, screenPts);
	        
	        // TODO: put colors as resources elsewhere to tidy code
	        // set the inner color
	        Paint color1 = new Paint();
	        color1.setAntiAlias(true);
	        color1.setColor(Color.RED);
	        color1.setStyle(Paint.Style.STROKE);
	        
	        // set the outer color
	        Paint color2 = new Paint();
	        color2.setAntiAlias(true);
	        color2.setColor(0x55EEEEEE);
	        color2.setStyle(Paint.Style.FILL);
	        
	        //---add the range circle ---
	        float radius = lastFocused.getRadius();

	        // adjust radius to scale
	        // in cylindrical projection (such as Mercator) the radius is scaled by the secant of the longitude
	        float pixelRadius  = mapView.getProjection().metersToEquatorPixels(radius);
	        pixelRadius /= (float) Math.cos(Math.toRadians(p.getLatitudeE6()  * 1E6));
	
	        
	        // draw outer circle
	        canvas.drawCircle(screenPts.x, screenPts.y, pixelRadius, color1);
	        
	        // draw filling
	        canvas.drawCircle(screenPts.x, screenPts.y, pixelRadius, color2);
	        
	        
	        // TODO: animation part
	        // TODO: make wave radius dependent on the start time
	        // TODO: make the period of generating waves more realistic
	        
	        Paint color3 = new Paint();
	        color3.setAntiAlias(true);
	        color3.setColor(Color.BLUE);
	        color3.setStyle(Paint.Style.STROKE);
	        color3.setAlpha(128);
	        color3.setStrokeWidth(3);
	        
	        // calculate the number of waves that we can show on screen
	        int iterations = Math.round(
	        		(float) Math.floor(radius / (lastFocused.getSpeed() * lastFocused.getPeriod())));
	        // calculate the total number of waves the earthquake has created
	        if (animTime == -1)
	        	animTime = when;
	        float totalIterations = (float) Math.floor((lastFocused.getTapTime() + (when - animTime) / 1000) /
	        								lastFocused.getPeriod());
	        
	        // adjustment factor to calculate the current position of the wave
	        float fract = (float) (lastFocused.getAge() / lastFocused.getPeriod() - totalIterations);
	        
	        float waveRadius = (float) (fract * lastFocused.getSpeed() * lastFocused.getPeriod());
	        float pixelWaveRadius = 0;
	        
	        // draw the circles
	        for (int i = 0; i <= iterations; i++) {
	        	pixelWaveRadius = mapView.getProjection().metersToEquatorPixels(waveRadius);
	        	pixelWaveRadius /= (float) Math.cos(Math.toRadians(p.getLatitudeE6()  * 1E6));
		        if (pixelWaveRadius > pixelRadius)
		        	break;
		        canvas.drawCircle(screenPts.x, screenPts.y, pixelWaveRadius, color3);
		        waveRadius += lastFocused.getSpeed() * lastFocused.getPeriod();

	        }
	    }
        
        // call the superclass method to draw other markers
        super.draw(canvas, mapView, shadow);
        
        return true;
	}
	
	/* 
	 * Adds an overlay item to the itemized overlay
	 */
	public void addQuakeOverlay(QuakeOverlayItem item) {
		myOverlays.add(item);
	}
}
